/*
 * Copyright 2010 elvish render Team
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 * http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef EI_MESSAGE_H
#define EI_MESSAGE_H

/** \brief The module for message passing.
 * \file ei_message.h
 * \author Elvic Liang
 */

#include <eiCORE/ei_core.h>
#include <eiCORE/ei_rect.h>

#ifdef __cplusplus
extern "C" {
#endif

/* network/interprocess message types */
enum {
	EI_MSG_BEGIN = 0,
	EI_MSG_UNKNOWN,

	/* a group of messages for minimizing network transfer overhead. */
	EI_MSG_GROUP,

	/* manager wants to disconnect the server. */
	EI_MSG_REQ_DISCONNECT,
	/* manager wants the server to create rendering threads. */
	EI_MSG_REQ_CREATE_THREADS,
	/* link statement */
	EI_MSG_REQ_LINK,
	/* manager set the current scene for rendering. */
	EI_MSG_REQ_SET_SCENE,
	/* manager end the current scene for rendering. */
	EI_MSG_REQ_END_SCENE,
	/* update the current scene for rendering. */
	EI_MSG_REQ_UPDATE_SCENE,
	/* server wants manager to allocate a tag. */
	EI_MSG_REQ_ALLOCATE_TAG,
	/* manager wants the server to process a job. */
	EI_MSG_REQ_PROCESS_JOB,
	/* manager wants the server to create a data. */
	EI_MSG_REQ_CREATE_DATA,
	/* manager wants the server to delete a data. */
	EI_MSG_REQ_DELETE_DATA,
	/* manager requests a data generated by the server,
	   or server requests a data from the manager. */
	EI_MSG_REQ_SEND_DATA,
	/* changes has been made to a data, flush the local copy of the data,
	   effectively force the data to be re-read over the network. */
	EI_MSG_REQ_FLUSH_DATA,
	/* server thread requests to check for abort. */
	EI_MSG_REQ_CHECK_ABORT,
	/* server thread steps progress. */
	EI_MSG_REQ_STEP_PROGRESS,

	/* generic information, for synchronization. */
	EI_MSG_INF_GENERIC,
	/* the server has been allocated with a host ID by the manager. */
	EI_MSG_INF_HOST_ALLOCATED,
	/* server thread tells the manager it was connected and authorized. */
	EI_MSG_INF_HOST_AUTHORIZED,
	/* manager tells the server that the tag was allocated. */
	EI_MSG_INF_TAG_ALLOCATED,
	/* the data has been generated at a host. */
	EI_MSG_INF_DATA_GENERATED,
	/* server tells the manager that rendering threads have been created. */
	EI_MSG_INF_THREAD_CREATED,
	/* server informs the manager that it has finished execution of a job. */
	EI_MSG_INF_JOB_FINISHED,
	/* manager tells the server whether rendering was aborted. */
	EI_MSG_INF_IS_ABORTED,
	/* the current information of the data to be received. */
	EI_MSG_INF_DATA_INFO,

	/* start processing an image bucket. */
	EI_MSG_BUCKET_STARTED,
	/* finished processing an image bucket. */
	EI_MSG_BUCKET_FINISHED,

	EI_MSG_END,
};

typedef struct eiMsgGroup {
	/* the number of messages in this group. */
	eiUint		num_msgs;

} eiMsgGroup;

typedef struct eiMsgReqDisconnectParams {
	eiUint		padding;

} eiMsgReqDisconnectParams;

typedef struct eiMsgReqCreateThreadsParams {
	eiUint		padding;

} eiMsgReqCreateThreadsParams;

typedef struct eiMsgReqLinkParams {
	eiUint		padding;

} eiMsgReqLinkParams;

typedef struct eiMsgReqSetSceneParams {
	eiTag		scene_tag;

} eiMsgReqSetSceneParams;

typedef struct eiMsgReqEndSceneParams {
	eiUint		padding;

} eiMsgReqEndSceneParams;

typedef struct eiMsgReqUpdateSceneParams {
	eiUint		padding;

} eiMsgReqUpdateSceneParams;

typedef struct eiMsgReqAllocateTagParams {
	eiHostID	host;

} eiMsgReqAllocateTagParams;

typedef struct eiMsgReqProcessJobParams {
	/* tag of the job to be processed. */
	eiTag		job;

} eiMsgReqProcessJobParams;

typedef struct eiMsgReqCreateDataParams {
	eiInt		type;
	eiUint		size;
	eiInt		flag;
	/* the tag of the data at manager side,
	   server must create the data with the
	   same tag, otherwise, a fatal error
	   occurs. */
	eiTag		data_tag;
	/* the host that created the data. */
	eiHostID	host;

} eiMsgReqCreateDataParams;

typedef struct eiMsgReqDeleteDataParams {
	/* tag of the data to be deleted. */
	eiTag		data;
	/* the host that deleted the data. */
	eiHostID	host;

} eiMsgReqDeleteDataParams;

typedef struct eiMsgReqSendDataParams {
	/* tag of the requested data. */
	eiTag		data;
	/* whether the sender should defer the 
	   data generation, if yes, the received 
	   data may be un-initialized, and we need 
	   to initialize it by ourselves(we are requester), 
	   otherwise, the data is initialized, data generator 
	   must have been called from the sender side. 
	   most of the time, for performance sake, we 
	   want to defer the initialization on sender side, 
	   to balance the workload of rendering manager, 
	   and only the host which uses the data will 
	   initialize it. */
	eiBool		defer_init;

} eiMsgReqSendDataParams;

typedef struct eiMsgReqFlushDataParams {
	/* tag of the flushing data. */
	eiTag		data;
	/* the host that is flushing the data. */
	eiHostID	host;

} eiMsgReqFlushDataParams;

typedef struct eiMsgReqCheckAbortParams {
	eiUint		padding;

} eiMsgReqCheckAbortParams;

typedef struct eiMsgReqStepProgressParams {
	eiUint		count;

} eiMsgReqStepProgressParams;

typedef struct eiMsgInfGenericParams {
	/* generic result of network requests. */
	eiBool		result;

} eiMsgInfGenericParams;

typedef struct eiMsgInfHostAllocatedParams {
	/* checksum for verifying whether the client is
	   really our rendering manager. */
	eiInt		checksum1;
	/* the allocated host ID for server host. */
	eiHostID	host;
	/* the endianness of the manager. */
	eiInt		mgr_endian;

} eiMsgInfHostAllocatedParams;

typedef struct eiMsgInfHostAuthorizedParams {
	/* checksum for verifying whether the server is
	   really our rendering server. */
	eiInt		checksum2;
	/* the authorization result. */
	eiBool		result;
	/* whether we need byte-swap for the host. */
	eiBool		need_byteswap;

} eiMsgInfHostAuthorizedParams;

typedef struct eiMsgInfTagAllocatedParams {
	eiTag		tag;

} eiMsgInfTagAllocatedParams;

typedef struct eiMsgInfDataGeneratedParams {
	/* tag of the generated data. */
	eiTag		data;
	/* the host that generated the data. */
	eiHostID	host;

} eiMsgInfDataGeneratedParams;

typedef struct eiMsgInfThreadCreatedParams {
	/* the number of rendering threads on the server. */
	eiUint		num_threads;

} eiMsgInfThreadCreatedParams;

typedef struct eiMsgInfJobFinishedParams {
	/* the resulting status of job execution. */
	eiInt		result;

} eiMsgInfJobFinishedParams;

typedef struct eiMsgInfIsAbortedParams {
	/* whether the rendering was aborted. */
	eiBool		abort;

} eiMsgInfIsAbortedParams;

typedef struct eiMsgInfDataInfoParams {
	/* the current size of the data to be received,
	   the data may have been resized, this case
	   should be handled. */
	eiUint		size;
	/* the initialization state of the data to be
	   received. */
	eiBool		inited;

} eiMsgInfDataInfoParams;

typedef struct eiMsgBucketStartedParams {
	/* the tag of the bucket rendering job. */
	eiTag		job;
	eiInt		pos_i, pos_j;
	eiRect4i	rect;
	eiHostID	host;

} eiMsgBucketStartedParams;

typedef struct eiMsgBucketFinishedParams {
	/* the tag of the bucket rendering job. */
	eiTag		job;
	/* on which host this job was executed and finished. */
	eiHostID	hostId;
	/* on which thread this job was executed and finished. */
	eiThreadID	threadId;
	eiInt		pos_i, pos_j;
	eiRect4i	rect;

} eiMsgBucketFinishedParams;

/* network rendering message */
typedef struct eiMessage {
	eiInt								type;
	union {
		eiMsgGroup						group;
		eiMsgReqDisconnectParams		disconnect_params;
		eiMsgReqCreateThreadsParams		create_threads_params;
		eiMsgReqLinkParams				link_params;
		eiMsgReqSetSceneParams			set_scene_params;
		eiMsgReqEndSceneParams			end_scene_params;
		eiMsgReqUpdateSceneParams		update_scene_params;
		eiMsgReqAllocateTagParams		allocate_tag_params;
		eiMsgReqProcessJobParams		process_job_params;
		eiMsgReqCreateDataParams		create_data_params;
		eiMsgReqDeleteDataParams		delete_data_params;
		eiMsgReqSendDataParams			send_data_params;
		eiMsgReqFlushDataParams			flush_data_params;
		eiMsgReqCheckAbortParams		check_abort_params;
		eiMsgReqStepProgressParams		step_progress_params;

		eiMsgInfGenericParams			generic_params;
		eiMsgInfHostAllocatedParams		host_allocated_params;
		eiMsgInfHostAuthorizedParams	host_authorized_params;
		eiMsgInfTagAllocatedParams		tag_allocated_params;
		eiMsgInfDataGeneratedParams		data_generated_params;
		eiMsgInfThreadCreatedParams		thread_created_params;
		eiMsgInfJobFinishedParams		job_finished_params;
		eiMsgInfIsAbortedParams			is_aborted_params;
		eiMsgInfDataInfoParams			data_info_params;

		eiMsgBucketStartedParams		bucket_started_params;
		eiMsgBucketFinishedParams		bucket_finished_params;
	};
} eiMessage;

eiCORE_API void ei_msg_init(eiMessage *msg);
eiCORE_API void ei_msg_set(eiMessage *msg, const eiInt type);

eiCORE_API void ei_byteswap_msg(eiMessage * const msg);

void ei_byteswap_msg_req_disconnect(eiMsgReqDisconnectParams * const params);
void ei_byteswap_msg_req_create_threads(eiMsgReqCreateThreadsParams * const params);
void ei_byteswap_msg_req_link(eiMsgReqLinkParams * const params);
void ei_byteswap_msg_req_set_scene(eiMsgReqSetSceneParams * const params);
void ei_byteswap_msg_req_end_scene(eiMsgReqEndSceneParams * const params);
void ei_byteswap_msg_req_update_scene(eiMsgReqUpdateSceneParams * const params);
void ei_byteswap_msg_req_allocate_tag(eiMsgReqAllocateTagParams * const params);
void ei_byteswap_msg_req_process_job(eiMsgReqProcessJobParams * const params);
void ei_byteswap_msg_req_create_data(eiMsgReqCreateDataParams * const params);
void ei_byteswap_msg_req_delete_data(eiMsgReqDeleteDataParams * const params);
void ei_byteswap_msg_req_send_data(eiMsgReqSendDataParams * const params);
void ei_byteswap_msg_req_flush_data(eiMsgReqFlushDataParams * const params);
void ei_byteswap_msg_req_check_abort(eiMsgReqCheckAbortParams * const params);
void ei_byteswap_msg_req_step_progress(eiMsgReqStepProgressParams * const params);

void ei_byteswap_msg_inf_generic(eiMsgInfGenericParams * const params);
void ei_byteswap_msg_inf_host_allocated(eiMsgInfHostAllocatedParams * const params);
void ei_byteswap_msg_inf_host_authorized(eiMsgInfHostAuthorizedParams * const params);
void ei_byteswap_msg_inf_tag_allocated(eiMsgInfTagAllocatedParams * const params);
void ei_byteswap_msg_inf_data_generated(eiMsgInfDataGeneratedParams * const params);
void ei_byteswap_msg_inf_thread_created(eiMsgInfThreadCreatedParams * const params);
void ei_byteswap_msg_inf_job_finished(eiMsgInfJobFinishedParams * const params);
void ei_byteswap_msg_inf_is_aborted(eiMsgInfIsAbortedParams * const params);
void ei_byteswap_msg_inf_data_info(eiMsgInfDataInfoParams * const params);

#ifdef __cplusplus
}
#endif

#endif

